package com.xes.attend.service.impl;


import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import com.xes.attend.domain.*;
import com.xes.attend.repo.AttendDataDao;
import com.xes.attend.service.AttendDataService;
import com.xes.attend.utils.HttpClientUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joda.time.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.util.UriTemplate;

import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class AttendDataServiceImpl implements AttendDataService {

	private static final Logger log = LogManager.getLogger(AttendDataServiceImpl.class);

	// 考勤服务器地址
	@Value("${info.attend_server_base_url}")
	private String attendBaseUrl;
	// 考勤刷卡提前时间
	@Value("${info.ams_core_getswipetime_url}")
	private String getSwipeTimeUrl;
	// 下载预考勤地址
	@Value("${info.ams_core_prepare_url}")
	private String downloadAttendDataUrl;
	// 下载增量考勤地址
	@Value("${info.ams_core_increase_url}")
	private String downloadAttendDynamicDataUrl;
	// 提交考勤结果
	@Value("${info.ams_core_feedback_url}")
	private String uploadAttendDataUrl;
	
	@Autowired
	private AttendDataDao attendDataDao;

	/**
	 * 动态下载预考勤数据的上次时间戳
	 */
	private Date dynamicDownloadDate = null;

	
	@Override
	@Transactional
	public AttendResult downloadAttendDataByToday(String venueId) throws BaseException {
		AttendResult result = new AttendResult(0, null, null);
		Result<List<AttendVo>> retData = null;
		Integer count = 0;
		
		if (StringUtils.isBlank(venueId)) {
			log.error("预考勤数据下载失败,教学点ID为空.");
			return result;
		}
		
		log.info("预考勤当日数据下载中>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
		
		try {
			Map<String, String> vars = ImmutableMap.of("venueId", venueId);
			URI expandUri = new UriTemplate(attendBaseUrl + downloadAttendDataUrl).expand(vars);
			String responseBody = HttpClientUtil.httpGet(expandUri.toString(), null, null);
			ObjectMapper objectMapper = new ObjectMapper();
			objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
			
			retData = objectMapper.readValue(responseBody, new TypeReference<Result<List<AttendVo>>>() {});
			if (retData!= null && retData.isSuccess()) {
				for (AttendVo dataVo : retData.getData()) {
					AttendData newData = new AttendData();
					BeanUtils.copyProperties(dataVo, newData);
					attendDataDao.save(newData);
					count++;
				}
			} else {
				log.error("预考勤当日数据下载失败: " + retData.getMessage());
				result.setMessage(retData.getMessage());
				return result;
			}
		} catch (Exception e) {
			log.error("预考勤当日数据下载失败: " + e.getMessage());
			log.debug(e);
			result.setMessage(e.getMessage());
			return result;
		}
		
		log.info("预考勤当日数据下载完成，共下载预考勤数据:{}", count);
		result.setDataCount(count);
		result.setDatetime(null);
		result.setMessage("预考勤当日数据下载完成");
		return result;
	}

	@Override
	@Transactional
	public AttendResult downloadAttendDynamicDataByToday(String venueId) throws BaseException {
		AttendResult result = new AttendResult(0, null, null);
		Result<TodayAttendanceIncreaseBean> retData = null;
		Integer count = 0;
		
		if (StringUtils.isBlank(venueId)) {
			log.error("动态考勤当日数据下载失败,教学点ID为空.");
			return result;
		}
		
		log.info("动态考勤当日数据下载中>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		if (dynamicDownloadDate == null) {
			dynamicDownloadDate = new Date();
		}
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("venueId", venueId));
		params.add(new BasicNameValuePair("timestamp", sf.format(dynamicDownloadDate)));
		String responseBody = HttpClientUtil.httpGet(attendBaseUrl + downloadAttendDynamicDataUrl, null, params);
		try {
			ObjectMapper objectMapper = new ObjectMapper();
			objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
			retData = objectMapper.readValue(responseBody, new TypeReference<Result<TodayAttendanceIncreaseBean>>() {});
			if (retData.isSuccess()) {
				TodayAttendanceIncreaseBean increaseData = retData.getData();
				result.setDatetime(sf.parse(increaseData.getEndDate()));
				
				for (AttendVo dataVo : increaseData.getIncreaseData()) {
					AttendData oldData = attendDataDao.findById(dataVo.getId());
					if (oldData != null) {
						oldData.setIsDelete(1);
						oldData.setModifyDate(new Date());
						attendDataDao.save(oldData);
					}
					AttendData newData = new AttendData();
					BeanUtils.copyProperties(dataVo, newData);
					attendDataDao.save(newData);
					count++;
				}
			} else {
				log.error("动态考勤当日数据返回失败: " + retData.getMessage());
				result.setMessage(retData.getMessage());
				return result;
			}
		} catch (Exception e) {
			log.error("动态考勤当日数据下载失败: " + e.getMessage());
			log.debug(e);
			result.setMessage(e.getMessage());
			return result;
		}
		
		log.info("动态考勤当日数据下载完成，共下载预考勤数据:{}", count);
		result.setDataCount(count);
		result.setDatetime(null);
		result.setMessage("动态考勤当日数据下载完成");
		return result;
	}
	
	@Override
	public Integer queryAttendTimeBegin(String areaCode, Integer timeDefault) throws BaseException {
		Integer attendTimeBegin = timeDefault;
		Map<String, String> vars = ImmutableMap.of("areaCode", areaCode);
		URI expandUri = new UriTemplate(attendBaseUrl + getSwipeTimeUrl).expand(vars);
		String responseBody = HttpClientUtil.httpGet(expandUri.toString(), null, null);
		try {
			ObjectMapper objectMapper = new ObjectMapper();
			objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
			Result<Integer> result = null;
			result = objectMapper.readValue(responseBody, new TypeReference<Result<Integer>>() {});
			if (result.isSuccess()) {
				attendTimeBegin = result.getData();
			} else {
				log.error("考勤开课前刷卡时间下载失败: " + result.getMessage());
			}
		} catch (Exception e) {
			log.error("考勤开课前刷卡时间下载失败: " + e.getMessage());
			log.debug(e);
		}
		return attendTimeBegin;
	}

	@Override
	@Transactional
	public AttendResult uploadAttendDataByToday(String venueId) throws BaseException {
		AttendResult result = new AttendResult(0, null, null);
		Result<List<CommitResultBean>> servResult = null;
		Integer count = 0;
		
		try {
			SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			List<AttendData> attendData = attendDataDao.findByIsSend(1);
			
			log.info("当日考勤数据上传中--------------------------------");
			
			// 组装上传数据
			List<AttendCommit> attendCommitList = new ArrayList<AttendCommit>();
			for (AttendData data : attendData) {
				AttendCommit attendCommit = new AttendCommit();
				attendCommit.setId(data.getId());
				attendCommit.setIsAbsent(data.getIsAbsent());
				attendCommit.setIsPunctual(data.getIsPunctual());
				attendCommit.setLaterTimes(data.getLaterTimes());
				attendCommit.setLaterType(data.getLaterType());
				attendCommit.setModifyTime(sf.format(data.getModifyDate()));
				attendCommitList.add(attendCommit);
				log.info("上传考勤数据:{},{},{}", data.getStuName(), data.getStuId(), attendCommit);
			}
			ObjectMapper objectMapper = new ObjectMapper();
			String jsonStr = objectMapper.writeValueAsString(attendCommitList);
			// 上传数据到服务器
			String responseBody = HttpClientUtil.httpPost(attendBaseUrl + uploadAttendDataUrl, jsonStr);
			// 获取服务器返回值
			servResult = objectMapper.readValue(responseBody, new TypeReference<Result<List<CommitResultBean>>>() {});
			// 如果成功，则修改数据为已上传
			if (servResult.isSuccess()) {
				for (AttendData data : attendData) {
					data.setIsSend(0);
					attendDataDao.save(data);
					count++;
				}
			} else {
				log.error("当日考勤数据上传失败: " + servResult.getMessage());
				result.setMessage(servResult.getMessage());
				return result;
			}
		} catch (Exception e) {
			log.error("当日考勤数据上传失败:{}", e.getMessage());
			log.debug(e);
			result.setMessage(servResult.getMessage());
			return result;
		}
		
		result.setDataCount(count);
		result.setDatetime(null);
		result.setMessage("当日考勤数据上传完成");
		return result;
	}

	@Override
	@Transactional
	public Result<AttendData> doAttend(String card_num, String venueId, Integer beginTime) throws BaseException {
		Result<AttendData> result = null;
		// 卡号为空[]
		if (StringUtils.isBlank(card_num)) {
			throw new BaseException(ResultCode.NONE_CARD_NUMBER);
		}
		// 缓存为null
		if (attendDataDao.count() == 0) {
			throw new BaseException(ResultCode.NONE_ATTEND_DATA);
		}
		List<AttendData> dataList = attendDataDao.findByAttendanceCardNum(card_num);
		// 缓存不为null,但没有数据。
		if (dataList.size() == 0) {
			throw new BaseException(ResultCode.NONE_ATTEND_RECORD);
		}
		// 有数据，分析处理数据。
		else {
			result = dataCheck(dataList, beginTime);
			if (result.isSuccess()) {
				AttendData attend = result.getData();
				attend.setModifyDate(new Date());
				attend.setIsAbsent(0);
				attend.setIsSend(1);
				attend.setIsIcsSend(1);
				if (result.getCode() == ResultCode.COURSE_IS_STARTED.getCode()) {
					attend.setLaterType(2);
					attend.setIsPunctual(1);
				}
				attendDataDao.save(attend);
				log.info(attend.getStuName() + "考勤成功\n" + attend.toString());
			}
		}
		return result;
	}

	/***
	 * 时间判断数据检查
	 * 
	 * @author wjl
	 * @title: dataCheck
	 * @date 2015年12月3日 下午3:49:27
	 * @param dataList
	 *            预考勤数据
	 * @param beginClassLimit
	 *            开课前刷卡限定
	 * @return Result<AttendData> 返回结果集
	 */
	private Result<AttendData> dataCheck(List<AttendData> dataList, Integer beginClassLimit) {
		
		Result<AttendData> result = new Result<AttendData>();
		
		Collections.sort(dataList, new Comparator<AttendData>() {
			public int compare(AttendData a, AttendData b) {
				// 按照上课时间进行排序
				return a.getStartTime().compareTo(b.getStartTime());
			}
		});
		
		for (int i = 0; i < dataList.size(); i++) {
			// 非删除的数据
			if (!dataList.get(i).getIsDelete().equals(1)) {
				// 上课日期
				String calssDate = DateFormatUtils.format(dataList.get(i).getClassDate(), "yyyy-MM-dd");
				// 上课开始时间
				String calssBeginTime = DateFormatUtils.format(dataList.get(i).getStartTime(), "HH:mm:ss");
				// 上课结束时间
				String calssEndTime = DateFormatUtils.format(dataList.get(i).getEndTime(), "HH:mm:ss");
				
				try {
					// 上课开始日期和时间
					Date classBeginDate = DateUtils.parseDate(calssDate + " " + calssBeginTime, "yyyy-MM-dd HH:mm:ss");
					// 上课结束日期和时间
					Date classEndDate = DateUtils.parseDate(calssDate + " " + calssEndTime, "yyyy-MM-dd HH:mm:ss");
					// 当前时间
					Date attend_now = new Date();
					
					double begin_delta = (attend_now.getTime() - classBeginDate.getTime()) / (1000 * 60.0);
					double end_delta = (attend_now.getTime() - classEndDate.getTime()) / (1000 * 60.0);
					
					if (begin_delta < 0 && -begin_delta > beginClassLimit) {
						// 上课前N小时外刷卡
						result.setSuccess(false);
						result.setCode(ResultCode.BEFOR_COURSE_STARTED.getCode());
						result.setMessage(ResultCode.BEFOR_COURSE_STARTED.getMessage());
						break;
					} else if (begin_delta < 0 && -begin_delta < beginClassLimit) {
						// 上课前N小时内刷卡
						result.setSuccess(true);
						result.setCode(ResultCode.SUCCESS.getCode());
						result.setMessage(ResultCode.SUCCESS.getMessage());
						result.setData(dataList.get(i));
						break;
					} else if (begin_delta >= 0 && end_delta <= 0) {
						// 开课后刷卡
						result.setSuccess(true);
						result.setCode(ResultCode.COURSE_IS_STARTED.getCode());
						result.setMessage(ResultCode.COURSE_IS_STARTED.getMessage());
						result.setData(dataList.get(i));
						break;
					} else if (i == dataList.size() -1 && end_delta >= 0) {
						// 所有课程已经结束
						result.setSuccess(false);
						result.setCode(ResultCode.COURSE_IS_END.getCode());
						result.setMessage(ResultCode.COURSE_IS_END.getMessage());
						break;
					}

				} catch (ParseException e) {
					log.error("上课日期和时间转换失败:{}", e.getMessage());
				}
			}
		}
		return result;
	}

	public static void main(String[] args) {
		try {
			// 正数表示 距离上课时间还有多少时间
			// 负数表示超过上课时间多少时间
			String class_date = "2015-12-02";
			String class_time = "20:00:00";
			Date today = new Date();
			Date classDate = DateUtils.parseDate(class_date + " " + class_time, "yyyy-MM-dd HH:mm:ss");
			DateTime classDate2 = new DateTime(classDate);// 上课时间
			DateTime dt1 = new DateTime(today);// 现在时间
			System.out.println(today.equals(classDate));

			System.out.println(Days.daysBetween(dt1, classDate2).getDays() + " 天 ");
			System.out.println(Hours.hoursBetween(dt1, classDate2).getHours() % 24 + " 小时 ");
			System.out.println(Minutes.minutesBetween(dt1, classDate2).getMinutes() % 60 + " 分钟 ");
			System.out.println(Seconds.secondsBetween(dt1, classDate2).getSeconds() % 60 + " 秒.");
			System.out.println(Seconds.secondsBetween(dt1, classDate2).getSeconds() % 60000 + " 毫秒.");

			System.out.println("NumberUtils:" + NumberUtils.toInt(null, 60));
		} catch (ParseException e) {
			e.printStackTrace();
		}

	}

	@Override
	public Boolean cleanAttendData(String venueId) throws BaseException {
		return true;
	}

	@Override
	public Boolean modifyAttendData(String venueId, String cardNum, AttendVo data) throws BaseException {
		return null;
	}

}
